<?php declare(strict_types=1);
/**
 * @author      xianganyall <xianganyall@gmail.com>
 * @copyright   2023-2025 owner
 **/

namespace Srv\Libs\Plugins\QrCode;

class QrBitStream
{
    private array $data = [];

    public function size(): int
    {
        return count($this->data);
    }

    private function allocate(int $setLength): void
    {
        $this->data = array_fill(0, $setLength, 0);
    }

    private static function newFromNum(int $bits, int $num): self
    {
        $__class__ = static::class;
        $QrBitStream = new $__class__();
        $QrBitStream->allocate($bits);
        $mask = 1 << ($bits - 1);
        for ($i = 0; $i < $bits; $i++) {
            if ($num & $mask) {
                $QrBitStream->data[$i] = 1;
            } else {
                $QrBitStream->data[$i] = 0;
            }
            $mask = $mask >> 1;
        }
        return $QrBitStream;
    }

    private static function newFromBytes(int $size, array $data): self
    {
        $__class__ = static::class;
        $QrBitStream = new $__class__();
        $QrBitStream->allocate($size * 8);
        $p = 0;
        for ($i = 0; $i < $size; $i++) {
            $mask = 0x80;
            for ($j = 0; $j < 8; $j++) {
                if ($data[$i] & $mask) {
                    $QrBitStream->data[$p] = 1;
                } else {
                    $QrBitStream->data[$p] = 0;
                }
                $p++;
                $mask = $mask >> 1;
            }
        }
        return $QrBitStream;
    }

    public function append(QRbitstream $arg): int
    {
        if ($arg->size() === 0) return 0;
        if ($this->size() === 0) {
            $this->data = $arg->data;
            return 0;
        }
        $this->data = array_values(array_merge($this->data, $arg->data));
        return 0;
    }

    public function appendNum(int $bits, int $num): int
    {
        if ($bits === 0) return 0;
        $b = self::newFromNum($bits, $num);
        $ret = $this->append($b);
        unset($b);
        return $ret;
    }

    public function appendBytes(int $size, array $data): int
    {
        if ($size === 0) return 0;
        $b = self::newFromBytes($size, $data);
        return $this->append($b);
    }

    public function toByte(): array
    {
        $size = $this->size();
        if ($size === 0) return [];
        $data = array_fill(0, intval(($size + 7) / 8), 0);
        $bytes = intval($size / 8);
        $p = 0;
        for ($i = 0; $i < $bytes; $i++) {
            $v = 0;
            for ($j = 0; $j < 8; $j++) {
                $v = $v << 1;
                $v |= $this->data[$p];
                $p++;
            }
            $data[$i] = $v;
        }
        if ($size & 7) {
            $v = 0;
            for ($j = 0; $j < ($size & 7); $j++) {
                $v = $v << 1;
                $v |= $this->data[$p];
                $p++;
            }
            $data[$bytes] = $v;
        }
        return $data;
    }
}